<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Task;

use App\Model\Game;
use App\Model\Play;
use App\Model\PlayDatum;
use App\Model\Team;
use App\Utils\Enum;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\DbConnection\Db;
use Hyperf\Filesystem\FilesystemFactory;
use Hyperf\Guzzle\ClientFactory;
use League\Flysystem\FilesystemException;
use PhpParser\Node\Expr\Isset_;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function Hyperf\Support\env;

class SyncMatchListTask extends AbstractBaseTask
{
    protected Client $client;

    public function execute(): void
    {
        $this->client = $this->container->get(ClientFactory::class)->create();
        $_start = time();
        $success = 0;
        $retryTimes = 0;
        $maxRetryTimes = 3;
        while ($retryTimes < $maxRetryTimes) {
            $data = $this->api->getMatchList(1, env('SYNC_ONCE_MATCH_NUM'));
            if (!$data || $data['code'] !== '0000' || empty($data['data']['records'])) {
                $this->sleep(3);
                continue;
            }
            $success = 0;
            foreach ($data['data']['records'] as $match) {
                if ($this->_syncMatch($match)) {
                    $success++;
                }
            }
            break;
        }
        $this->logger->warning(sprintf('成功同步 %d 场比赛 耗时:%d s', $success, time() - $_start));
    }


    /**
     * 同步比赛.
     * @param mixed $basicMatchInfo
     */
    private function _syncMatch($basicMatchInfo): bool
    {
        $match = Play::query()->where('match_id', $basicMatchInfo['matchId'])->first();
        if ($match) {
            $this->logger->info("比赛({$basicMatchInfo['matchId']}) 已存在");
            $update = [];
            if ($match['match_time'] != $basicMatchInfo['matchTime']) {
                $update['match_time'] = $basicMatchInfo['matchTime'];
            }
            if (!empty($basicMatchInfo['videoUrl']) && $match['video_url'] != $basicMatchInfo['videoUrl']) {
                $update['video_url'] = $basicMatchInfo['videoUrl'];
            }
            if (!empty($update)) {
                Play::query()->where('id', $match['id'])->update($update);
                $this->logger->info("「比赛」「{$match['game_name']}」{$match['home_team_name']} VS {$match['away_team_name']} 更新成功");
                return true;
            }
            return false;
        }

        $data = $this->api->getMatchInfo($basicMatchInfo['matchId']);
        if (!$data || $data['code'] !== '0000') {
            return false;
        }
        $detail = $data['data']['result'];
        $gameInfo = $this->_getGameInfo($basicMatchInfo);
        $homeTeam = $this->_getTeamInfo($detail['match']['homeTeam']);
        $awayTeam = $this->_getTeamInfo($detail['match']['awayTeam']);
        if (!$homeTeam || !$awayTeam) {
            $this->logger->warning('球队信息获取失败');
            return false;
        }
        $match = Play::create([
            'match_id' => $basicMatchInfo['matchId'],
            'sport_id' => $basicMatchInfo['sportId'],
            'game_id' => $gameInfo['game_id'],
            'game_name' => $gameInfo['name'],
            'game_logo' => $gameInfo['logo'],
            'home_team_id' => $homeTeam['team_id'],
            'home_team_name' => $homeTeam['name'],
            'home_team_logo' => $homeTeam['logo'],
            'home_team_scores' => new_json_encode($basicMatchInfo['homeScores'] ?? []),
            'home_double_teams' => $homeTeam['groups'],
            'away_team_id' => $awayTeam['team_id'],
            'away_team_name' => $awayTeam['name'],
            'away_team_logo' => $awayTeam['logo'],
            'away_team_scores' => new_json_encode($basicMatchInfo['awayScores'] ?? []),
            'away_double_teams' => $awayTeam['groups'],
            'vb_scores' => new_json_encode($basicMatchInfo['vbScores'] ?? []),
            'match_time' => $basicMatchInfo['matchTime'],
            'match_status' => $basicMatchInfo['matchStatus'],
            'match_status_id' => $basicMatchInfo['statusId'],
            'sp_match_status' => $detail['match']['spMatchStatus'] ?? 0,
            'video_url' => $basicMatchInfo['videoUrl'] ?? '',
            'animate_url' => !empty($detail['animation']['url']) ? $detail['animation']['url'] : '',
            'tennis_scores' => new_json_encode(!empty($detail['result']['tennisScores']) ? $detail['result']['tennisScores'] : []),
            'tennis_double_flag' => $homeTeam['is_group'],
            'sync_time' => time(),
        ]);
        if (!$match) {
            $this->logger->error('「比赛」写入失败 ' . new_json_encode($match));
            return false;
        }
        PlayDatum::create([
            'match_id' => $match['match_id'],
        ]);
        $this->logger->warning("「比赛」「{$match['game_name']}」{$match['home_team_name']} VS {$match['away_team_name']} 同步成功");
        $this->sleep(0.3);
        return true;
    }

    /**
     * 获取球队信息.
     * @param array $targetTeamInfo
     * @return array|false
     */
    private function _getTeamInfo(array $targetTeamInfo): array|false
    {
        $team = Team::query()->where('team_id', $targetTeamInfo['id'])->first();
        if (!$team) {
            $doubleTeams = [];
            if (isset($targetTeamInfo['doubleTeams']) && is_array($targetTeamInfo['doubleTeams'])) {
                foreach ($targetTeamInfo['doubleTeams'] as $t) {
                    $_team = $this->_getTeamInfo($t);
                    if ($_team) {
                        $doubleTeams[] = [
                            'id' => $_team['id'],
                            'name' => $_team['name'],
                            'logo' => $_team['logo'],
                            'stats' => $_team['stats'],
                            'sport_id' => $_team['sport_id'],
                        ];
                    }
                }
            }
            $logo = '';
            if (!empty($targetTeamInfo['logo'])) {
                $logo = $this->_syncFile($targetTeamInfo['logo'], Enum::ICON_TEAM_LOGO, $targetTeamInfo['sportId']);
            }

            $team = Team::create([
                'team_id' => $targetTeamInfo['id'],
                'name' => $targetTeamInfo['name'],
                'short_name' => $targetTeamInfo['shortName'] ?? $targetTeamInfo['name'],
                'logo' => $logo ?: '',
                'slug' => $targetTeamInfo['slug'] ?? '',
                'sport_id' => $targetTeamInfo['sportId'],
                'season_id' => $targetTeamInfo['seasonId'] ?? '',
                'is_group' => empty($doubleTeams) ? 0 : 1,
                'member_ids' => empty($doubleTeams) ? '' : implode(',', array_column($doubleTeams, 'id')),
                'groups' => new_json_encode($doubleTeams),
                'stats' => $targetTeamInfo['hasStats'] ?? 0,
                'add_time' => time(),
            ]);
            if ($team) {
                $this->logger->info("「球队」「{$team['name']}」采集成功");
            } else {
                $this->logger->warning("「球队」「{$team['name']}」采集失败");
                return false;
            }
        }
        return $team->toArray();
    }

    /**
     * 获取赛事信息.
     * @param $basicMatchInfo
     * @return array|false
     */
    private function _getGameInfo($basicMatchInfo): array|false
    {
        $game = Game::query()->where('game_id', $basicMatchInfo['competitionId'])->first();
        if (!$game) {
            $logo = '';
            if (!empty($basicMatchInfo['competitionLogo'])) {
                $logo = $this->_syncFile($basicMatchInfo['competitionLogo'], Enum::ICON_LEAGUE_LOGO, $basicMatchInfo['sportId']);
            }
            $game = Game::create([
                'game_id' => $basicMatchInfo['competitionId'],
                'sport_id' => $basicMatchInfo['sportId'],
                'name' => $basicMatchInfo['competitionName'],
                'logo' => $logo ?: '',
                'add_time' => time(),
            ]);
            if ($game) {
                $this->logger->info("「赛事」「{$game['name']}」同步成功");
            }
        }
        return $game ? $game->toArray() : false;
    }

    /**
     * 同步文件.
     * @param $fileUrl string 文件名称
     * @param $fileUrlType int 文件类型
     * @param $sportId int 运动类型
     */
    private function _syncFile($fileUrl, $fileUrlType, $sportId): mixed
    {
        if (empty($fileUrl)) {
            return false;
        }
        $fileUrl = $this->_fillImgUrl($fileUrl, $fileUrlType, $sportId);
        $syncFile = Db::table(Enum::TB_SYNC_FILES)->where('target_url', $fileUrl)->first();
        if (!empty($syncFile)) {
            return $syncFile['local_url'];
        }

        try {
            $res = $this->client->get($fileUrl, ['timeout' => 5]);
            if ($res->getStatusCode() == 200) {
                $this->logger->info('「附件」' . $fileUrl . ' 下载成功');
                $ext = fileext($fileUrl);
                $filename = date('Y/md/') . md5($fileUrl) . '.' . $ext;
                try {
                    $cos = $this->container->get(FilesystemFactory::class)->get('cos');
                    $cos->write('img/' . $filename, $res->getBody()->getContents());
                    Db::table(Enum::TB_SYNC_FILES)->insert([
                        'local_url' => $filename,
                        'target_url' => $fileUrl,
                        'fileext' => $ext,
                        'filesize' => 0,
                        'add_time' => time(),
                    ]);
                    $this->logger->info('「附件」' . $filename . ' 同步COS成功');
                    return $filename;
                } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
                    $this->logger->error('附件保存同步失败:' . $e->getMessage(), func_get_args());
                }
            } else {
                $this->logger->warning('附件下载失败:http_code=' . $res->getStatusCode(), func_get_args());
            }
        } catch (FilesystemException|GuzzleException $e) {
            $this->logger->warning('syncFile fail:' . $e->getMessage());
        }
        return $fileUrl;
    }

    /**
     * 补全图片地址
     * @param mixed $url
     * @param mixed $imgType
     * @param mixed $sportId
     */
    private function _fillImgUrl($url, $imgType, $sportId): string
    {
        if (!empty($url)) {
            // 去除oss图片处理函数
            $url = preg_replace('/!w\d+$/i', '', $url);
        }
        if (str_starts_with($url, 'http')) {
            return $url;
        }
        $urlPrefix = env('TARGET_OSS_URL', '');
        if ($imgType == Enum::ICON_LEAGUE_LOGO) {
            switch ($sportId) {
                case Enum::SPORT_FOOTBALL:
                    $urlPrefix .= '/football/competition/';
                    break;
                case Enum::SPORT_BASKETBALL:
                    $urlPrefix .= '/basketball/competition/';
                    break;
                case Enum::SPORT_TENNIS:
                    $urlPrefix .= '/v2/unique-tournament/';
                    break;
                case Enum::SPORT_VOLLEYBALL:
                    $urlPrefix .= '/volleyball/competition/';
                    break;
            }
        } elseif ($imgType == Enum::ICON_TEAM_LOGO) {
            switch ($sportId) {
                case Enum::SPORT_FOOTBALL:
                    $urlPrefix .= '/football/team/';
                    break;
                case Enum::SPORT_BASKETBALL:
                    $urlPrefix .= '/basketball/team/';
                    break;
                case Enum::SPORT_TENNIS:
                    $urlPrefix .= '/v2/team/';
                    break;
                case Enum::SPORT_VOLLEYBALL:
                    $urlPrefix .= '/volleyball/team/';
                    break;
            }
        }
        return $urlPrefix . $url;
    }
}